<?php

namespace common\Token;

use app\common\model\Admin;
use app\api\model\Member;
use common\exception\ExceptionEmun;
use common\exception\TokenException;
use common\helper\CurlHelper;
use common\traits\InstanceTrait;
use Firebase\JWT\JWT;

;

/**
 * Class Token
 * JWT处理类
 */
class Token
{
    use InstanceTrait;
    /**
     * @var JWT
     */
    protected $_config;
    protected $_user_info = null;
    protected $_pk = null;
    /**
     * @var PayLoad
     */
    protected $_payload = null;

    /**
     * Token constructor.
     * @param string $token
     * @throws TokenException
     * @throws \ReflectionException
     */
    public function __construct($token = '')
    {
        if ($token) {
            $this->verifyToken($token);
        }
        $this->_config = config('jwt_config.');
    }

    /**
     * 生成token
     * @param $member_info 需要带上用户Model
     * @return string
     */
    public function generateToken($member_info)
    {
        $this->_pk = $member_info->getPk();
        $payload = $this->_config['payload'];
        $payload['pk'] = $member_info[$this->_pk];
        //用反射获取类名
        $ReflectionObject = new \ReflectionObject($member_info);
        $payload['model'] = $ReflectionObject->getName();
        $payload['phone'] = $member_info['phone'];
        $token = JWT::encode($payload, $this->_config['salt'], $this->_config['alg']);
        $this->verifyToken($token);
        return $token;
    }

    /**
     * @param PayLoad $payload
     */
    public function setPayload($payload)
    {
        $this->_payload = $payload;
    }

    /**
     * @return PayLoad
     */
    public function getPayload()
    {
        return $this->_payload;
    }

    /**
     * 小程序获取Token
     * @param $code
     * @return string
     * @throws TokenException
     * @throws \ReflectionException
     */
    public function wx_program_login($code)
    {
        $mini_program_config = config('mini_program_config.');
        $app_id = $mini_program_config['app_id'];
        $app_secret = $mini_program_config['app_secret'];
        $grant_type = $mini_program_config['grant_type'];
        $login_url_template = $mini_program_config['login_url'];
        $login_url = sprintf(
            $login_url_template,
            $app_id, $app_secret, $code, $grant_type
        );
        $result = CurlHelper::curl_get($login_url);
        $wx_ret_arr = json_decode($result, true);

        //无结果
        if (empty($wx_ret_arr)) {
            ExceptionEmun::throwException(TokenException::ERROR_WX_API);
        }
        //结果有错误
        $login_fail = array_key_exists('errcode', $wx_ret_arr);
        if ($login_fail) {
            ExceptionEmun::throwException(TokenException::ERROR_WX_API, $wx_ret_arr['errmsg']);
        }
        //是否保存数据,生成token
        $openid = $wx_ret_arr['openid'];
        $user_info = Member::instance()->getByOpenid($openid);
        if (!$user_info) {
            $data = ['open_id', $openid];
            Member::instance()->save($data);
            $user_info = Member::instance()->getByOpenid($openid);
        }
        return $this->generateToken($user_info);
    }

    /**
     * 验证token合法性
     * @param $token
     * @return Member
     * @throws TokenException
     * @throws \ReflectionException
     */
    public function verifyToken($token)
    {
        $timestamp = time();
        if (empty($token)) {
            ExceptionEmun::throwException(TokenException::ILLEGAL_TOKEN);
        }
        //解析token
        $this->_payload = JWT::decode($token, $this->_config['salt'], [$this->_config['alg']]);
        if (empty($this->_payload)) {
            ExceptionEmun::throwException(TokenException::ILLEGAL_TOKEN);
        }
        if (isset($this->_payload->exp_time) && ($timestamp - JWT::$leeway) >= $this->_payload->exp_time) {
            ExceptionEmun::throwException(TokenException::EXPIRED_TOKEN);
        }

        return $this->setUserInfo($this->_payload);
    }

    /**
     * 设置登录用户信息
     * @param $payload
     */
    public function setUserInfo($payload)
    {
        if (!empty($payload)) {
            $model = new $payload->model();
            $member_info = $model->cache(true, 60)->get($this->_payload->pk);
        }
        $member_info->hidden(['password', 'create_time', 'update_time']);
        return $this->_user_info = $member_info;
    }

    /**
     * 获取登陆用户信息,只要有$this->_payload就能读取
     * @return Member | Admin
     * @throws TokenException
     */
    public function getUserInfo()
    {
        return $this->_user_info;
    }


}


